﻿Option Explicit On

Imports System.Windows.Controls.Canvas

Public Class clsHolder
    Public Event Move(ByVal sender As Object)

    Private WithEvents m_Shape As Rectangle
    Private WithEvents m_LLine As clsLine
    Private WithEvents m_RLine As clsLine

    Private m_Ancestor As shpConnector
    Private m_Editor As shpEditor
    Private m_Key As String
    Private m_PntClick As Point
    Private m_Left As Double, m_Top As Double

#Region "properties"
    Public ReadOnly Property ClickX() As Integer
        Get
            ClickX = m_PntClick.X
        End Get
    End Property

    Public ReadOnly Property ClickY() As Integer
        Get
            ClickY = m_PntClick.Y
        End Get
    End Property

    Public Property Shape() As Rectangle
        Get
            Return m_Shape
        End Get
        Set(ByVal value As Rectangle)
            m_Shape = value
        End Set
    End Property

    Public Property LeftLine() As clsLine
        Get
            Return m_LLine
        End Get
        Set(ByVal value As clsLine)
            m_LLine = value
            If m_LLine IsNot Nothing Then
                m_LLine.LeftHolder = Me
            End If
        End Set
    End Property

    Public Property RightLine() As clsLine
        Get
            Return m_RLine
        End Get
        Set(ByVal value As clsLine)
            m_RLine = value
            If m_RLine IsNot Nothing Then
                m_RLine.RightHolder = Me
            End If
        End Set
    End Property

    Public ReadOnly Property Key() As String
        Get
            Key = m_Key
        End Get
    End Property

    Public ReadOnly Property Ancestor() As shpConnector
        Get
            Ancestor = m_Ancestor
        End Get
    End Property

    Public ReadOnly Property LeftHolder() As clsHolder
        Get
            If m_LLine Is Nothing Then
                Return Nothing
            Else
                Return m_LLine.Neighbor(Me)
            End If
        End Get
    End Property

    Public ReadOnly Property RightHolder() As clsHolder
        Get
            If m_RLine Is Nothing Then
                Return Nothing
            Else
                Return m_RLine.Neighbor(Me)
            End If
        End Get
    End Property

    Public ReadOnly Property nLeft() As Double
        Get
            nLeft = m_Left
        End Get
    End Property

    Public ReadOnly Property nTop() As Double
        Get
            nTop = m_Top
        End Get
    End Property
#End Region

    Public Sub New(ByVal par As shpConnector, ByVal key As String, Optional ByVal pre As clsLine = Nothing, Optional ByVal nxt As clsLine = Nothing)
        Dim itmRemove As New MenuItem, img As New Image
        m_Key = key
        m_Ancestor = par
        m_Editor = GetEditor(par)
        If pre IsNot Nothing Then
            LeftLine = pre
        End If
        If nxt IsNot Nothing Then
            RightLine = nxt
        End If
        m_Shape = New Rectangle
        m_Shape.Fill = ColorHolder
        m_Shape.Stroke = ColorStroke
        m_Shape.Height = c_HolderHeight
        m_Shape.Width = c_HolderWidth
        m_Shape.RadiusX = c_HolderRadius
        m_Shape.RadiusY = c_HolderRadius
        m_Shape.Visibility = Windows.Visibility.Hidden
        m_Shape.Focusable = True
        SetLeft(m_Shape, 0) : SetTop(m_Shape, 0)
        itmRemove.Header = "Remove Connector"
        img.Source = New BitmapImage(New Uri("ico\remove.png", UriKind.Relative))
        img.Width = 16 : img.Height = 16
        itmRemove.Icon = img
        m_Shape.ContextMenu = New ContextMenu
        m_Shape.ContextMenu.Items.Add(itmRemove)
        AddHandler itmRemove.Click, AddressOf itmRemove_Click
    End Sub

    Public Sub MoveShape(Optional ByVal x As Integer = -1, Optional ByVal y As Integer = -1, Optional ByVal depth As Integer = 0)
        Dim h1 As clsHolder = Nothing, h2 As clsHolder = Nothing, lef As Integer, top As Integer
        If m_LLine IsNot Nothing Then
            h1 = m_LLine.Neighbor(Me)
        End If
        If m_RLine IsNot Nothing Then
            h2 = m_RLine.Neighbor(Me)
        End If
        lef = GetLeft(m_Shape)
        top = GetTop(m_Shape)
        If m_Ancestor.Ortogonal And depth = 0 Then
            If Not Imposition(Me, x, y) Then
                Exit Sub
            End If
        End If
        If x > -1 And x <> lef Then
            If depth < 2 Then
                SetLeft(m_Shape, x)
                If m_Ancestor.Ortogonal Then
                    If h1 IsNot Nothing Then
                        If GetLeft(h1.Shape) = lef Then
                            If h1.IsLast And depth = 0 Then
                                m_Ancestor.SplitLine(c_AxisX, h1, Me)
                            Else
                                h1.MoveShape(x, , depth + 1)
                            End If
                        End If
                    End If
                    If h2 IsNot Nothing Then
                        If GetLeft(h2.Shape) = lef Then
                            If h2.IsLast And depth = 0 Then
                                m_Ancestor.SplitLine(c_AxisX, Me, h2)
                            Else
                                h2.MoveShape(x, , depth + 1)
                            End If
                        End If
                    End If
                End If
            End If
        End If
        If y > -1 And y <> top Then
            If depth < 2 Then
                SetTop(m_Shape, y)
                If m_Ancestor.Ortogonal Then
                    If h1 IsNot Nothing Then
                        If GetTop(h1.Shape) = top Then
                            If h1.IsLast And depth = 0 Then
                                m_Ancestor.SplitLine(c_AxisY, h1, Me)
                            Else
                                h1.MoveShape(, y, depth + 1)
                            End If
                        End If
                    End If
                    If h2 IsNot Nothing Then
                        If GetTop(h2.Shape) = top Then
                            If h2.IsLast And depth = 0 Then
                                m_Ancestor.SplitLine(c_AxisY, Me, h2)
                            Else
                                h2.MoveShape(, y, depth + 1)
                            End If
                        End If
                    End If
                End If
            End If
        End If
        ' округление до целых из-за погрешности в масштабировании
        m_Left = CInt(GetLeft(m_Shape) / m_Editor.ZoomX)
        m_Top = CInt(GetTop(m_Shape) / m_Editor.ZoomY)
        RaiseEvent Move(Me)
    End Sub

    Public Function Imposition(ByVal hol As clsHolder, ByVal x As Integer, ByVal y As Integer) As Boolean
        Dim lh As clsHolder = Nothing, rh As clsHolder = Nothing, ll As clsLine = Nothing, rl As clsLine = Nothing
        Dim lh2 As clsHolder = Nothing, rh2 As clsHolder = Nothing, ll2 As clsLine = Nothing, rl2 As clsLine = Nothing
        Dim lx As Integer, rx As Integer, lx2 As Integer, rx2 As Integer, x1 As Integer
        Dim ly As Integer, ry As Integer, ly2 As Integer, ry2 As Integer, y1 As Integer
        Imposition = False
        ll = hol.LeftLine : rl = hol.RightLine
        x1 = GetLeft(hol.Shape) : y1 = GetTop(hol.Shape)
        If ll IsNot Nothing Then
            lh = ll.Neighbor(Me)
        End If
        If rl IsNot Nothing Then
            rh = rl.Neighbor(Me)
        End If
        If lh IsNot Nothing Then
            lx = GetLeft(lh.Shape) : ly = GetTop(lh.Shape)
            If (lx = x And ly = y) Or (lx = x1 And ly = y) Or (lx = x And ly = y1) Then
                Exit Function
            Else
                ll2 = lh.Neighbor(ll)
                If ll2 IsNot Nothing Then
                    lh2 = ll2.Neighbor(lh)
                End If
                If lh2 IsNot Nothing Then
                    lx2 = GetLeft(lh2.Shape) : ly2 = GetTop(lh2.Shape)
                    If lx = x1 Then
                        If lx2 = x And ly2 = ly Then
                            Exit Function
                        End If
                    ElseIf ly = y1 Then
                        If lx2 = lx And ly2 = y Then
                            Exit Function
                        End If
                    End If
                End If
            End If
        End If
        If rh IsNot Nothing Then
            rx = GetLeft(rh.Shape) : ry = GetTop(rh.Shape)
            If (rx = x And ry = y) Or (rx = x1 And ry = y) Or (rx = x And ry = y1) Then
                Exit Function
            Else
                rl2 = rh.Neighbor(rl)
                If rl2 IsNot Nothing Then
                    rh2 = rl2.Neighbor(rh)
                End If
                If rh2 IsNot Nothing Then
                    rx2 = GetLeft(rh2.Shape) : ry2 = GetTop(rh2.Shape)
                    If rx = x1 Then
                        If rx2 = x And ry2 = ry Then
                            Exit Function
                        End If
                    ElseIf ry = y1 Then
                        If rx2 = rx And ry2 = y Then
                            Exit Function
                        End If
                    End If
                End If
            End If
        End If
        Imposition = True
    End Function

    Public Function Neighbor(ByVal hol As clsLine) As clsLine
        Neighbor = Nothing
        If hol Is m_LLine Then
            Neighbor = m_RLine
        ElseIf hol Is m_RLine Then
            Neighbor = m_LLine
        End If
    End Function

    Public Function IsLast() As Boolean
        If m_LLine Is Nothing Or m_RLine Is Nothing Then
            IsLast = True
        Else
            IsLast = False
        End If
    End Function

    Public Sub Zoom()
        SetLeft(m_Shape, CInt(m_Left * m_Editor.ZoomX))
        SetTop(m_Shape, CInt(m_Top * m_Editor.ZoomY))
        RaiseEvent Move(Me)
    End Sub

#Region "events"
    Private Sub LeftLine_Move(ByVal sender As Object) Handles m_LLine.Move
        Dim x As Integer, y As Integer
        x = m_LLine.Shape.X1 : y = m_LLine.Shape.Y1
        If RightLine IsNot Nothing Then
            m_RLine.Shape.X2 = x : m_RLine.Shape.Y2 = y
        End If
        SetLeft(m_Shape, x - m_Shape.Width / 2)
        SetTop(m_Shape, y - m_Shape.Height / 2)
    End Sub

    Private Sub RightLine_Move(ByVal sender As Object) Handles m_RLine.Move
        Dim x As Integer, y As Integer
        x = m_RLine.Shape.X2 : y = m_RLine.Shape.Y2
        If LeftLine IsNot Nothing Then
            m_LLine.Shape.X1 = x : m_LLine.Shape.Y1 = y
        End If
        SetLeft(m_Shape, x - m_Shape.Width / 2)
        SetTop(m_Shape, y - m_Shape.Height / 2)
    End Sub

    Private Sub shpHolder_MouseLeftButtonDown(ByVal sender As Object, ByVal e As MouseButtonEventArgs) Handles m_Shape.MouseLeftButtonDown
        m_PntClick.X = e.GetPosition(m_Shape).X : m_PntClick.Y = e.GetPosition(m_Shape).Y
        m_Editor.DragObject = Me
    End Sub

    Private Sub shpHolder_MouseLeftButtonUp(ByVal sender As Object, ByVal e As MouseButtonEventArgs) Handles m_Shape.MouseLeftButtonUp
        m_PntClick.X = e.GetPosition(m_Shape).X : m_PntClick.Y = e.GetPosition(m_Shape).Y
        If m_Ancestor.Ortogonal Then
            m_Ancestor.UnitLine()
        End If
        m_Editor.DragObject = Nothing
    End Sub

    Private Sub shpHolder_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) Handles m_Shape.MouseMove
        m_Shape.Cursor = Cursors.Hand
    End Sub

    Private Sub shpHolder_MouseEnter(ByVal sender As Object, ByVal e As MouseEventArgs) Handles m_Shape.MouseEnter
        m_Shape.StrokeThickness = c_BorderEnter
    End Sub

    Private Sub shpHolder_MouseLeave(ByVal sender As Object, ByVal e As MouseEventArgs) Handles m_Shape.MouseLeave
        m_Shape.StrokeThickness = c_BorderLeave
    End Sub

    Private Sub shpHolder_GotFocus(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles m_Shape.GotFocus
        SetZIndex(m_Shape, 1)
    End Sub

    Private Sub shpHolder_LostFocus(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles m_Shape.LostFocus
        SetZIndex(m_Shape, 0)
    End Sub

    Private Sub itmRemove_Click(ByVal sender As Object, ByVal e As EventArgs)
        m_Ancestor.Remove()
    End Sub
#End Region

End Class
